package com.doubledragon.tiny.ioc.xmlReader.impl;

import com.doubledragon.tiny.ioc.BeanDefinition;
import com.doubledragon.tiny.ioc.BeanReference;
import com.doubledragon.tiny.ioc.PropertyValue;
import com.doubledragon.tiny.ioc.io.ResourceLoad;
import com.doubledragon.tiny.ioc.xmlReader.AbstractXmlBeanDefinitionReader;
import org.springframework.util.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.doubledragon.tiny.ioc.PropertyValues;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;

/**
 * Created by dragon
 *
 * BeanDefinitionReader的实现类，读取配置文件后，创建BeanDefinition并注册
 */
public class XmlBeanDefinitionReader extends AbstractXmlBeanDefinitionReader {

    public XmlBeanDefinitionReader(ResourceLoad resourceLoad) {
        super(resourceLoad);
    }

    @Override
    public void loadBeanDefinition(String location) throws Exception {
        InputStream inputStream = getResourceLoad().getResource(location).getInputStream();
        doLoadBeanDefinition(inputStream);
    }

    private void doLoadBeanDefinition(InputStream inputStream) throws Exception{
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        Document document = documentBuilder.parse(inputStream);
        //解析出BeanDefinition并注册
        registerBeanDefinition(document);
    }

    private void registerBeanDefinition(Document document)  throws Exception {
        Element root = document.getDocumentElement();
        //解析Element
        parseBeanDefinition(root);
    }

    private void parseBeanDefinition(Element root)  throws Exception {
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                processBeanDefinition(ele);
            }
        }
    }

    private void processBeanDefinition(Element ele) throws Exception {
        String beanName = ele.getAttribute("name");
        String className = ele.getAttribute("class");
        Class<?> beanClass = Class.forName(className);

        //实例化BeanDefinition
        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setBeanName(beanName);
        beanDefinition.setBeanClass(beanClass);
        beanDefinition.setPropertyValues(new PropertyValues());
        //属性填充
        processProperty(ele, beanDefinition);

        //注册BeanDefinition
        getRegistry().put(beanName, beanDefinition);
    }

    private void processProperty(Element ele, BeanDefinition beanDefinition) {
        NodeList propertyNode = ele.getElementsByTagName("property");
        for (int i = 0; i < propertyNode.getLength(); i++) {
            Node node = propertyNode.item(i);
            if (node instanceof Element) {
                Element propertyEle = (Element) node;
                String name = propertyEle.getAttribute("name");
                String value = propertyEle.getAttribute("value");
                if(!StringUtils.isEmpty(value)){
                    beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
                }else{
                    //查找ref节点指向的bean
                    String ref = propertyEle.getAttribute("ref");
                    if(StringUtils.isEmpty(ref)){
                        throw new IllegalArgumentException("Configuration problem: <property> element for property '"
                                + name + "' must specify a ref or value");
                    }

                    //保存BeanReference，在创建Bean的时候，就需要判断是否是BeanDefinition还是BeanReference
                    //如果是BeanReference，就要去容器中寻找对应的bean或者去创建bean
                    BeanReference beanReference = new BeanReference(ref);
                    beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanReference));
                }

            }
        }
    }

}
